Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D

Namespace DgvFilterPopup


	''' <summary>
	''' The base class from which to derive effective <i>filter host</i> controls
	''' </summary>
	''' <remarks>
	''' The purpose of the <i>filter host</i> control is to show a popup near a right-clicked column and to 
	''' host child <i>column filter</i> controls. 
	''' When the popup is shown, only the <i>column filter</i> control related to right-clicked column
	''' is visibile. 
	''' <b>DgvBaseFilterHost</b> is a derivation of <b>UserControl</b> and provide functionalities to 
	''' cooperate with <see cref="DgvFilterManager"/>.  
	''' <para>
	''' NOTE: 
	''' This class must be intended as an abstract class. However, declaring it as abstract,
	''' would generate errors whitin the designer when designing derived classes.
	''' </para>
	''' <para>
	''' In your derivation, you have to provide a host area (such as a panel) and ovverride the 
	''' <see cref="DgvBaseFilterHost.FilterClientArea"/> to return it. Also, create visual elements 
	''' for <i>remove filter</i>, <i>remove all filters</i>, <i>apply filter</i> and use the 
    ''' <b>DgvFilterManager</b> methods  and 
	''' <see cref="DgvFilterManager.ActivateAllFilters "/>. 
	''' </para>
	''' </remarks>
	''' <example>
	''' <code>
	'''public partial class DgvFilterHost : DgvBaseFilterHost {
	'''
	'''    public DgvFilterHost() {
	'''        InitializeComponent();
	'''        this.CurrentColumnFilterChanged += new EventHandler(DgvFilterHost_CurrentColumnFilterChanged);
	'''    }
	''' 
	'''    void DgvFilterHost_CurrentColumnFilterChanged(object sender, EventArgs e) {
	'''        lblColumnName.Text = CurrentColumnFilter.OriginalDataGridViewColumnHeaderText;
	'''    }
	''' 
	'''    public override Control FilterClientArea {
	'''        get {
	'''            return this.panelFilterArea;
	'''        }
	'''    }
	''' 
	'''    private void tsOK_Click(object sender, EventArgs e) {
	'''        FilterManager.ActivateFilter(true);
	'''        this.Popup.Close();
	'''    }
	''' 
	'''    private void tsRemove_Click(object sender, EventArgs e) {
	'''        FilterManager.ActivateFilter(false);
	'''        this.Popup.Close();
	'''    }
	''' 
	'''    private void tsRemoveAll_Click(object sender, EventArgs e) {
	'''        FilterManager.ActivateAllFilters(false);
	'''        this.Popup.Close();
	'''    }
	''' 
	'''}
	''' </code>
	''' </example>
	Public Class DgvBaseFilterHost
		Inherits UserControl

		#Region "EVENTS"

		''' <summary>
		''' Occurs when the current visible <i>column filter</i> is changed.
		''' </summary>
		Public Event CurrentColumnFilterChanged As EventHandler

		#End Region


		#Region "PRIVATE FIELDS"

		Private mPopup As ToolStripDropDown
		Private mFilterManager As DgvFilterManager
		Private mCurrentColumnFilter As DgvBaseColumnFilter = Nothing
		Private mSizeDifference As Size

		#End Region


		#Region "PROPERTIES"


		''' <summary>
		''' Return the effective area to which <i>column filters</i> will be added.
		''' </summary>
		Public Overridable ReadOnly Property FilterClientArea() As Control
			Get
				Return Me
			End Get
		End Property



		''' <summary>
		''' Gets the <b>ToolStripDropDown</b> object used to popup the <i>filter host</i>
		''' </summary>
		Public ReadOnly Property Popup() As ToolStripDropDown
			Get
				If mPopup Is Nothing Then
					mPopup = New ToolStripDropDown()
					Dim ControlHost As New ToolStripControlHost(Me)
					ControlHost.Padding = Padding.Empty
					ControlHost.Margin = Padding.Empty
					ControlHost.AutoSize = False
					mPopup.Padding = Padding.Empty
					mPopup.Items.Add(ControlHost)
					mPopup.Region = Me.Region
				End If
				Return mPopup
			End Get
		End Property



		''' <summary>
		''' Gets or sets the <i>filter manger</i> 
		''' </summary>
		Public Property FilterManager() As DgvFilterManager
			Get
				Return mFilterManager
			End Get
			Set
				mFilterManager = value
			End Set
		End Property



		''' <summary>
		''' Gets or sets the currently visibile <i>column filter</i> control
		''' </summary> 
		Public Property CurrentColumnFilter() As DgvBaseColumnFilter
			Get
				Return mCurrentColumnFilter
			End Get
			Set
				' Called once: store the original size difference of the filterhost and the filterClientArea
				If mSizeDifference = Size.Empty Then
					mSizeDifference = System.Drawing.Size.Subtract(Me.Size, FilterClientArea.Size)
					Me.MinimumSize = Me.Size
				End If
				If mCurrentColumnFilter IsNot Nothing Then
					mCurrentColumnFilter.Visible = False
				End If
				mCurrentColumnFilter = value
				DoAutoFit()

                Dim e As New EventArgs()
                RaiseEvent CurrentColumnFilterChanged(Me, e)

                mCurrentColumnFilter.Visible = True
			End Set
		End Property


		''' <summary>
		''' Gets the original size difference of the <i>filter host</i> and the <see cref="DgvBaseFilterHost.FilterClientArea"/>.
		''' </summary>
		Public ReadOnly Property SizeDifference() As Size
			Get
				Return mSizeDifference
			End Get
		End Property

		#End Region


		#Region "HELPERS"

		''' <summary>
		''' Performs growing / shrinking of the <i>filter host</i> to best fit the current visibile <i>column filter</i>.
		''' </summary>
		''' <remarks>
		''' Ovverride this method to provide your own resize logic.
		''' </remarks>
		Protected Overridable Sub DoAutoFit()
			Dim NewHostSize As Size = Size.Add(mSizeDifference, mCurrentColumnFilter.Size)
            NewHostSize.Width = System.Math.Max(NewHostSize.Width, Me.MinimumSize.Width)
            NewHostSize.Height = System.Math.Max(NewHostSize.Height, Me.MinimumSize.Height)
			Me.Size = NewHostSize

			FilterClientArea.Size = Size.Subtract(NewHostSize, mSizeDifference)
			AlignFilter()
		End Sub

		''' <summary>
		''' Aligns the <i>column filter</i> into the filter client area.
		''' </summary>
		''' <remarks>
		''' Ovverride this method to provide your own alignment logic.
		''' </remarks>
		Protected Sub AlignFilter()
			Dim x As Integer = 0
			' VFilterAlignmentType.Left:
			Dim y As Integer = 0
			' HFilterAlignmentType.Top:
			Select Case mCurrentColumnFilter.VFilterAlignment
				Case VFilterAlignment.Right
					x = FilterClientArea.Width - mCurrentColumnFilter.Width
					Exit Select
				Case VFilterAlignment.Center
                    x = CInt((FilterClientArea.Width - mCurrentColumnFilter.Width) / 2)
					Exit Select
			End Select

			Select Case mCurrentColumnFilter.HFilterAlignment
				Case HFilterAlignment.Bottom
					y = FilterClientArea.Height - mCurrentColumnFilter.Height
					Exit Select
				Case HFilterAlignment.Middle
                    y = CInt((FilterClientArea.Height - mCurrentColumnFilter.Height) / 2)
					Exit Select
			End Select
			mCurrentColumnFilter.Location = New Point(x, y)
		End Sub

		''' <summary>
		''' Returns a region based on the transparency color of a bitmap.
		''' </summary>
		''' <param name="bitmap">The bitmap.</param>
		''' <param name="transparencyColor">The transparency color.</param>
		''' <returns>A region</returns>
		Public Shared Function BitmapToRegion(bitmap As Bitmap, transparencyColor As Color) As Region
			If bitmap Is Nothing Then
				Throw New ArgumentNullException("Bitmap", "Bitmap cannot be null!")
			End If

			Dim height As Integer = bitmap.Height
			Dim width As Integer = bitmap.Width

			Dim path As New GraphicsPath()

			For j As Integer = 0 To height - 1
				For i As Integer = 0 To width - 1
					If bitmap.GetPixel(i, j) = transparencyColor Then
						Continue For
					End If

					Dim x0 As Integer = i

					While (i < width) AndAlso (bitmap.GetPixel(i, j) <> transparencyColor)
						i += 1
					End While

					path.AddRectangle(New Rectangle(x0, j, i - x0, 1))
				Next
			Next

			Dim region As New Region(path)
			path.Dispose()
			Return region
		End Function

		''' <summary>
		''' Registers the a combo box.
		''' </summary>
		''' <param name="comboBox">The combo box.</param>
		''' <remarks>
		''' When the user clicks on an <b>ComboBox</b> item that is outside of the
		''' host area, this cause an unwanted closing of the <i>filter host</i>. 
		''' If you use a <b>ComboBox</b> in a customized <i>column filter</i>, 
		''' be sure to call this method in your filter intitialitazion code.
		''' </remarks>
		Public Sub RegisterComboBox(comboBox As ComboBox)
			AddHandler comboBox.DropDown, New EventHandler(AddressOf onDropDown)
			AddHandler comboBox.DropDownClosed, New EventHandler(AddressOf onDropDownClosed)
		End Sub

		Private Sub onDropDown(sender As Object, e As EventArgs)
			Me.Popup.AutoClose = False
		End Sub

		Private Sub onDropDownClosed(sender As Object, e As EventArgs)
			Me.Popup.AutoClose = True
		End Sub

		#End Region


	End Class
End Namespace
